using System;
using System.Drawing;
using System.Runtime.InteropServices;

namespace FileSystemCustomClasses.Framework
{
	//TODO: 011 - Hide extern methods behind public wrappers
	public static class NativeMethods
	{
		private static object _iconLock = new object(); 
		private const uint SHGFI_LARGEICON = 0x0; // 'Large icon
		private const uint SHGFI_SMALLICON = 0x1; // 'Small icon
		private const uint SHGFI_ICON = 0x100;
		private const uint SHGFI_TYPENAME = 0x400;

		[DllImport("shell32")]
		public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

		[DllImport("kernel32")]
		private static extern int DeviceIoControl(IntPtr hDevice, int dwIoControlCode, ref short lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped);

		[DllImport("user32")]
		private static extern bool DestroyIcon(IntPtr handle);

		/// <summary>Enumerate shares (NT)</summary>
		[DllImport("netapi32", CharSet = CharSet.Unicode)]
		private static extern int NetShareEnum(string lpServerName, int dwLevel,
			out IntPtr lpBuffer, int dwPrefMaxLen, out int entriesRead,
			out int totalEntries, ref int hResume);

		/// <summary>Enumerate shares (9x)</summary>
		[DllImport("svrapi", CharSet = CharSet.Ansi)]
		private static extern int NetShareEnum(
			[MarshalAs(UnmanagedType.LPTStr)] string lpServerName, int dwLevel,
			IntPtr lpBuffer, ushort cbBuffer, out ushort entriesRead,
			out ushort totalEntries);

		/// <summary>Free the buffer (NT)</summary>
		[DllImport("netapi32")]
		private static extern int NetApiBufferFree(IntPtr lpBuffer);

		[StructLayout(LayoutKind.Sequential)]
		public struct SHFILEINFO
		{
			public IntPtr hIcon;
			public IntPtr iIcon;
			public uint dwAttributes;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
			public string szDisplayName;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
			public string szTypeName;
		};

		/**************************************************************************************************************************************************/

		public static Icon GetFileIcon(string argFullPath)
		{
			//Multi-threading SHGetFileInfo makes it fail.
			lock(_iconLock)
			{
				SHFILEINFO shellInfo = new SHFILEINFO();

				IntPtr iconHandle = SHGetFileInfo(argFullPath, 0, ref shellInfo, (uint)Marshal.SizeOf(shellInfo), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_TYPENAME);

				Icon smallIcon = (Icon)Icon.FromHandle(shellInfo.hIcon).Clone();
				DestroyIcon(shellInfo.hIcon);

				return smallIcon;
			}
		}

	}
}
